//! # Fop - 文件操作 Api 模块
//!
//! 文件操作 Api 模块用于进行文件操作。

use std::{
    fs::{create_dir_all, remove_file, File},
    io::{BufReader, BufWriter},
    path::PathBuf,
};

use glob::{glob_with, MatchOptions};
use serde_json::Value;

use crate::util::get_data_dir;

/// 进行文件的模糊搜索
///
/// # 参数
///
/// * `patterns` - 模糊搜索模式
/// * `case_sensitive` - 是否区分大小写
/// * `require_literal_separator` - 是否要求模式中包含一个斜杠
/// * `require_literal_leading_dot` - 是否要求模式中包含一个点
///
/// # 返回值
///
/// * [`Vec<PathBuf>`] - 匹配的文件路径
///
/// # 示例
///
/// ```
/// let paths = glob(vec!["*.mid".to_string()], None, None, None);
/// ```
#[tauri::command]
pub fn glob(
    patterns: Vec<String>,
    case_sensitive: Option<bool>,
    require_literal_separator: Option<bool>,
    require_literal_leading_dot: Option<bool>,
) -> Vec<PathBuf> {
    log::info!("模糊搜索 {patterns:?}");

    let mut ret = Vec::new();

    // glob 参数
    let mut match_options = MatchOptions::new();
    match_options.case_sensitive = case_sensitive.unwrap_or(match_options.case_sensitive);
    match_options.require_literal_separator =
        require_literal_separator.unwrap_or(match_options.require_literal_separator);
    match_options.require_literal_leading_dot =
        require_literal_leading_dot.unwrap_or(match_options.require_literal_leading_dot);

    // 依次 glob 所有 pattern
    for pattern in patterns {
        match glob_with(&pattern, match_options.clone()) {
            Ok(t) => {
                for entry in t {
                    match entry {
                        Ok(path) => {
                            ret.push(path);
                        }
                        Err(e) => {
                            log::error!("glob 错误：{e:?}");
                        }
                    }
                }
            }
            Err(e) => {
                log::error!("glob 样式 {pattern} 发生错误：{e:?}");
            }
        }
    }

    ret
}

/// 在 Data 目录下进行模糊搜索
///
/// # 参数
///
/// * `patterns` - 模糊搜索模式
/// * `case_sensitive` - 是否区分大小写
/// * `require_literal_separator` - 是否要求模式中包含一个斜杠
/// * `require_literal_leading_dot` - 是否要求模式中包含一个点
///
/// # 返回值
///
/// * [`Vec<PathBuf>`] - 匹配的文件路径
///
/// # 示例
///
/// ```
/// let paths = glob_in_data(vec!["*.mid".to_string()], None, None, None);
/// ```
#[tauri::command]
pub fn glob_in_data(
    patterns: Vec<String>,
    case_sensitive: Option<bool>,
    require_literal_separator: Option<bool>,
    require_literal_leading_dot: Option<bool>,
) -> Vec<PathBuf> {
    log::info!("在 Data 目录下进行模糊搜索 {patterns:?}");

    // 在 Data 目录下
    let dir = get_data_dir();

    // 转换路径
    let patterns = patterns
        .iter()
        .filter_map(|e| {
            let mut ret = dir.clone();
            ret.push(e);
            ret.to_str().map(|e| e.to_string())
        })
        .collect();

    // 进行 glob
    glob(
        patterns,
        case_sensitive,
        require_literal_separator,
        require_literal_leading_dot,
    )
}

/// 读取 Json 文件
///
/// # 参数
///
/// * `file_name` - 文件名
///
/// # 返回值
///
/// * [`Result<Value, String>`] - 结果
///
/// # 示例
///
/// ```
/// let value = read_json("config").unwrap();
/// ```
#[tauri::command]
pub fn read_json(file_name: String) -> Result<Value, String> {
    log::info!("读取 JSON 从 {file_name}");

    // 确认路径
    let mut target = get_data_dir();
    target.push(file_name);
    target.set_extension("json");

    // 读取 JSON
    let file = File::open(target).map_err(|e| e.to_string())?;
    let reader = BufReader::new(file);
    let value = serde_json::from_reader(reader).map_err(|e| e.to_string())?;

    Ok(value)
}

/// 写入 Json 文件
///
/// # 参数
///
/// * `file_name` - 文件名
/// * `value` - 值
///
/// # 返回值
///
/// * [`Result<(), String>`] - 结果
///
/// # 示例
///
/// ```
/// write_json("config", Value::Null).unwrap();
/// ```
#[tauri::command]
pub fn write_json(file_name: String, value: Value) -> Result<(), String> {
    log::info!("写入 JSON 到 {file_name}");

    // 确认路径
    let mut target = get_data_dir();
    target.push(file_name);
    target.set_extension("json");

    // 创建必要目录
    if let Some(file_dir) = target.parent() {
        create_dir_all(file_dir).map_err(|e| e.to_string())?;
    }

    // 写入 JSON
    let file = File::create(target).map_err(|e| e.to_string())?;
    let writer = BufWriter::new(file);
    serde_json::to_writer(writer, &value).map_err(|e| e.to_string())?;

    Ok(())
}

/// 移除 Json 文件
///
/// # 参数
///
/// * `file_name` - 文件名
///
/// # 返回值
///
/// * [`Result<(), String>`] - 结果
///
/// # 示例
///
/// ```
/// remove_json("config").unwrap();
/// ```
#[tauri::command]
pub fn remove_json(file_name: String) -> Result<(), String> {
    log::info!("删除 JSON 文件 {file_name}");

    // 确认路径
    let mut target = get_data_dir();
    target.push(file_name);
    target.set_extension("json");

    // 删除 JSON
    remove_file(target).map_err(|e| e.to_string())?;

    Ok(())
}

/// 重命名 Json 文件
#[tauri::command]
pub fn rename_json(old_name: String, new_name: String) -> Result<(), String> {
    log::info!("重命名 JSON 文件 {old_name} 到 {new_name}");

    // 原始路径
    let mut old_target = get_data_dir();
    old_target.push(old_name);
    old_target.set_extension("json");

    // 新路径
    let mut new_target = get_data_dir();
    new_target.push(new_name);
    new_target.set_extension("json");

    // 检查路径是否相同
    if old_target == new_target {
        return Ok(());
    }

    // 移动文件
    std::fs::rename(old_target, new_target).map_err(|e| e.to_string())?;

    Ok(())
}

/// 打开 Data 文件夹
///
/// # 返回值
///
/// * [`Result<(), String>`] - 结果
///
/// # 示例
///
/// ```
/// open_data_folder().unwrap();
/// ```
#[tauri::command]
pub fn open_data_folder() -> Result<(), String> {
    // 日志
    log::info!("打开 Data 文件夹");

    // 获取 Data 目录
    let path = get_data_dir();
    let path_str = path.to_str().ok_or(String::from("获取 Data 目录失败"))?;

    // 检查目录是否存在
    // 不存在则创建
    if !path.exists() {
        std::fs::create_dir_all(path_str).map_err(|e| e.to_string())?;
    }

    // 打开 Data 文件夹
    // 1. Windows
    if cfg!(target_os = "windows") {
        std::process::Command::new("explorer.exe")
            .arg(path_str)
            .spawn()
            .map_err(|e| e.to_string())?;
    }

    // 2. macOS
    if cfg!(target_os = "macos") {
        std::process::Command::new("open")
            .arg(path_str)
            .spawn()
            .map_err(|e| e.to_string())?;
    }

    // 3. Linux
    if cfg!(target_os = "linux") {
        std::process::Command::new("xdg-open")
            .arg(path_str)
            .spawn()
            .map_err(|e| e.to_string())?;
    }

    Ok(())
}
